{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "845f6ddc",
   "metadata": {},
   "source": [
    "# Animation\n",
    "\n",
    "\n",
    "<div align=\"center\"><img src=\"https://gitee.com/weiweqi/img_dw/raw/master/images/202112212251720.webp\" width=\"50%\"/></div>\n",
    "\n",
    "\n",
    "## 1. 概述\n",
    "\n",
    "在 [matplotlib.animation](https://matplotlib.org/stable/api/animation_api.html) 模块下有 4 个与绘制动画有关的类。分别为 `Animation`、`TimedAnimation`、`ArtistAnimation`、`FuncAnimation`。\n",
    "\n",
    "- `Animation`：所有类的基类\n",
    "- `TimedAnimation`： `Animation`的子类，用于绘制每间隔 1 毫秒绘制动画的一帧\n",
    "- `ArtistAnimation`：`TimedAnimation`的子类，用于利用一组已经创建好的 `Artist` 对象绘制动画\n",
    "- `FuncAnimation`：`TimedAnimation`的子类，通过重复调用 `fun()` 方法来绘制动画。\n",
    "\n",
    "<a href=\"https://matplotlib.org/stable/api/animation_api.html\">\n",
    "    <div align=\"center\"><img src=\"https://gitee.com/weiweqi/img_dw/raw/master/images/202112211922116.webp\" width=\"90%\"/></div>\n",
    "</a>\n",
    "\n",
    "**注意**\n",
    "\n",
    "- 必须将创建的动画存储在全局变量。否则，`Animation` 对象将被垃圾收集并停止动画。\n",
    "\n",
    "\n",
    "## 2. 绘制动画\n",
    "\n",
    "最简单的方式就是直接使用官方已经实现好了的 `ArtistAnimation` 和 `FuncAnimation` 两个子类。\n",
    "\n",
    "首先先了解基类的方法，因为基类有的方法子类肯定有。\n",
    "\n",
    "### Animation\n",
    "\n",
    "- `new_frame_seq()`：返回一个新的帧信息序列\n",
    "- `new_saved_frame_seq()`：返回保存/缓存的帧信息的新序列\n",
    "- `pause()`：暂停动画\n",
    "- `resume()`：恢复动画\n",
    "\n",
    "\n",
    "**保存动画**\n",
    "\n",
    "`Animation`提供了 3 个用于保存动画的方法：\n",
    "- ```python\n",
    "  save(filename, writer=None, fps=None, dpi=None, codec=None, \n",
    "      bitrate=None, extra_args=None, metadata=None, \n",
    "      extra_anim=None, savefig_kwargs=None, *, progress_callback=None)\n",
    "  ```\n",
    "- `to_html5_video(embed_limit=None)`\n",
    "  将将动画保存为 h264 视频，并以 base64 编码形式放到 html 的 `<video>` 标签中。默认`rcParams[\"animation.writer\"]='ffmpeg'` 和 `rcParams[\"animation.bitrate\"]=-1`\n",
    "  \n",
    "  - embed_limit：`float`，可选参数，限制视频的大小。默认为20，单位MB\n",
    "\n",
    "- `to_jshtml(fps=None, embed_frames=True, default_mode=None)`\n",
    "  将动画转换为 HTML 并显示\n",
    "  \n",
    "  - `fps`：`int`，可选，每秒显示的帧数，默认为None，表示动画的帧间隔的帧速率\n",
    "  - `embed_frames`：`bool`，可选\n",
    "  - `default_mode`：`str`，可选，动画结束后怎么办，`{'loop', 'once', 'reflect'}`，默认为 `'loop'`，表示一直循环显示动画\n",
    "\n",
    "\n",
    "### FuncAnimation\n",
    "\n",
    "**构造方法**\n",
    "\n",
    "```python\n",
    "matplotlib.animation.FuncAnimation(\n",
    "    fig, func, frames=None, init_func=None, fargs=None, \n",
    "    save_count=None, cache_frame_data=True, **kwargs)\n",
    "```\n",
    "\n",
    "参数\n",
    "\n",
    "- `fig`：`Figure`，必填参数，`Figure`的实例对象\n",
    "- `func`：`callable`，必填参数，每一帧都会调用该函数\n",
    "- `frames`：`iterable`, `int`, `generator`, or `None`，可选参数，传递func的数据源和动画的每一帧，一般是可迭代对象\n",
    "- `init_func`：`callable`，可选参数，初始化函数，在绘制动画前会被调用\n",
    "- `fargs`：`tuple` or `None`，可选参数，传递给 `func` 的参数\n",
    "- `save_count`：`int`，可选参数，默认为100，从帧到缓存的值数量的回退\n",
    "- `interval`：`int`，可选参数，默认为200，每一帧的延时，单位毫秒\n",
    "- `blitting`：`bool`，可选参数，默认为False，是否使用 blitting 来优化绘图\n",
    "- `cache_frame_data`：`bool`，可选参数，默认为True，是否缓存帧数据\n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ffa764c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "# 导入 matplotlib.animation 模块\n",
    "from IPython.display import display, HTML\n",
    "import matplotlib.animation as animation\n",
    "%matplotlib inline\n",
    "\n",
    "\n",
    "# 创建figure\n",
    "fig = plt.figure()\n",
    "# 向figure中添加axes子图\n",
    "ax = fig.add_subplot(111)\n",
    "ax.grid(linestyle=\"--\", alpha=0.8)\n",
    "# 创建两个列表用于保存数据\n",
    "xdata, ydata = [], []\n",
    "rng = np.random.default_rng()\n",
    "line, = ax.plot([], [])\n",
    "\n",
    "# 定义初始化函数\n",
    "def init():\n",
    "    ax.set_xlim(0, np.pi)\n",
    "    ax.set_ylim(-0.01, 1)\n",
    "    return line,\n",
    "\n",
    "# 定义数据生成器\n",
    "def data_gen():\n",
    "    for t in np.linspace(0.01, 4*np.pi, 200):\n",
    "        yield t, np.exp(-t**2) + rng.random()/(10+2*t)\n",
    "\n",
    "\n",
    "# 定义更新数据的函数，用于显示每一帧的数据\n",
    "def update(data):\n",
    "    t, y = data\n",
    "    xdata.append(t)\n",
    "    ydata.append(y)\n",
    "    \n",
    "    # 实时设置x轴刻度范围\n",
    "    xmin, xmax = ax.get_xlim()\n",
    "    if t > xmax:\n",
    "        ax.set_xlim(xmin, 1.5*xmax)\n",
    "        # ax.figure.canvas.draw()\n",
    "    \n",
    "    # 绘制数据\n",
    "    line.set_data(xdata, ydata)\n",
    "    \n",
    "    return line,\n",
    "\n",
    "# 创建动画对象\n",
    "ani = animation.FuncAnimation(fig, update, frames=data_gen,\n",
    "                    init_func=init, blit=True)\n",
    "# 在notebook中显示\n",
    "# 文件太大了无法显示，所有把输出给删除了\n",
    "HTML(ani.to_jshtml())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f13cabb9",
   "metadata": {},
   "source": [
    "输出截图\n",
    "\n",
    "<div align=\"center\"><img src=\"https://gitee.com/weiweqi/img_dw/raw/master/images/202112232038362.webp\" width=\"60%\"/></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb842b07",
   "metadata": {},
   "source": [
    "## ArtistAnimation\n",
    "\n",
    "**概述**\n",
    "\n",
    "相比`FuncAnimation`，`ArtistAnimation` 可以对每一帧绘制多个`artist`。\n",
    "\n",
    "\n",
    "**构造方法**\n",
    "\n",
    "```python\n",
    "matplotlib.animation.ArtistAnimation(\n",
    "    fig, artists, interval, repeat_delay, repeat, blit, **kwargs)\n",
    "```\n",
    "\n",
    "参数\n",
    "- `fig`：`Figure`，Figure的实例对象\n",
    "- `artists`：`list`，Artist实例对象列表\n",
    "- `interval`：`int`, 默认为200，动画中每一帧的延时时间，单位为毫秒\n",
    "- `repeat_delay`：`int`, 默认为0，连续动画运行之间的延迟，单位为毫秒\n",
    "- `repeat`：`bool`, 默认为True，当帧序列完成时动画是否重复\n",
    "- `blit`：`bool`, 默认为False，是否使用 blitting 来优化绘图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f142604",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "# 导入 matplotlib.animation 模块\n",
    "from IPython.display import display, HTML\n",
    "import matplotlib.animation as animation\n",
    "%matplotlib inline\n",
    "\n",
    "# 数据\n",
    "x = np.linspace(0, 2 * np.pi, 100)\n",
    "y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)\n",
    "\n",
    "# 创建figure\n",
    "fig = plt.figure()\n",
    "# 向figure中添加axes子图\n",
    "ax = fig.add_subplot(111)\n",
    "ax.set_axis_off()\n",
    "rng = np.random.default_rng()\n",
    "# 创建 artists 列表\n",
    "artists = []\n",
    "for i in range(10):\n",
    "    x += np.pi / 10\n",
    "    y += np.pi / 10\n",
    "    # 在一帧里面放两个 Artist，分别是 Text对象 和 AxesImage对象\n",
    "    frame = []\n",
    "    frame += [\n",
    "        ax.imshow(np.sin(x) + np.cos(y)),\n",
    "        ax.text(sum(ax.get_xlim())/8, sum(ax.get_ylim())/8, s=str(i), fontsize=25, color=\"orange\")\n",
    "    ]\n",
    "        \n",
    "    artists.append(frame)\n",
    "\n",
    "# 创建动画对象\n",
    "ani = animation.ArtistAnimation(fig, artists, blit=True)\n",
    "# 在notebook中显示\n",
    "# 文件太大了无法显示，所有把输出给删除了\n",
    "HTML(ani.to_jshtml())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "015914c0",
   "metadata": {},
   "source": [
    "输出截图\n",
    "\n",
    "<div align=\"center\"><img src=\"https://gitee.com/weiweqi/img_dw/raw/master/images/202112232039896.webp\" width=\"60%\"/></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9985dd1",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:visual] *",
   "language": "python",
   "name": "conda-env-visual-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.15"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
